#include "myc.h"

void deflocals() {
	struct vtype typ;
	struct var *var;
	typ.bt=pass_type();
	if (typ.bt==tsame) error("same is a pseudo type");
	while (!tokis(pass,";")) {
		typ.pt=0;
		typ.lv=1;
		while (tokis(pass,"*")) {
			typ.pt++;
			pass=pass->next;
		}
		if (tokis(pass,"&")) {
			typ.lv=2;
			pass=pass->next;
		}
		if (!is_letter(*pass->str)) error ("invalid var name");
		if (tokis(pass->next,"[")) {
			if (typ.lv==2) error("reference can't be array");
			typ.lv=0;
			int siz=size(typ);
			typ.pt++;
			if (!isdigit(*pass->next->next->str)) error("invalid array size");
			if (!tokis(pass->next->next->next,"]")) error("invalid array def");
			setstack(-siz*toktoint(pass->next->next));
			add_var(pass,typ,stack);
			pass=pass->next->next->next;
		} else {
			setstack(-(typ.lv>1?PTR_SIZE:size(typ)));
			var=add_var(pass,typ,stack);
		}
		if (tokis(pass->next,"=")) {
			if (typ.lv==0) error("can't assing array");
			struct token *end=find_leftest(pass,NULL,", ;");
			struct tree *tree=gettree(pass->next->next,end);
			assign_var(var,tree);
			freetree(tree);
			pass=end;
		} else pass=pass->next;
		if (tokis(pass,",")) pass=pass->next;
	}
	pass=pass->next;
}

void defglobs(int ext) {
	struct vtype typ;
	typ.bt=pass_type();
	if (typ.bt==tsame) error("same is a pseudo type");
	typ.lv=1;
	while (!tokis(pass,";")) {
		typ.pt=0;
		while (tokis(pass,"*")) {
			typ.pt++;
			pass=pass->next;
		}
		int siz=size(typ);
		if (!is_letter(*pass->str)) error ("invalid var name");
		add_var(pass,typ,0);
		pass=pass->next;
		if (tokis(pass,"[")) {
			lastvar->type.lv=0;
			lastvar->type.pt++;
			if (!isdigit(*pass->next->str)) error("invalid array size");
			if (!tokis(pass->next->next,"]")) error("invalid array def");
			siz*=toktoint(pass->next);
			pass=pass->next->next->next;
		}
		if (ext==1) ext_var(lastvar->name);
		else set_var(lastvar->name,siz);
		if (ext==2) glb_var(lastvar->name);
		if (tokis(pass,",")) pass=pass->next;
	}
	pass=pass->next;
}

void assign_var(struct var *var,struct tree *tree) {
	if (var->type.bt!=tree->type.bt || var->type.pt!=tree->type.pt) treerror(tree,"different types");
	if (var->type.lv-1>tree->type.lv) treerror(tree,"lvalue required");
	int si=PTR_SIZE;
	if (var->type.lv==1) si=size(tree->type);
	setstack(-PTR_SIZE-si);
	push_loc(var->loc);
	run_tree(tree,var->type.lv-1);
	setstack(2*PTR_SIZE+si);
	set_param(si);
	call_func("operass(same&,same)");
	setstack(si);
}

void run_stat() {
	struct token *end=find_leftest(pass,NULL,";");
	if (!end) error("missing ;");
	struct tree *tree=gettree(pass,end);
	if (!tree) {
		pass=end->next;
		return;
	}
	if (tree->next) error("unexpected ,");
	run_tree(tree,0);
	setstack(size(tree->type));
	freetree(tree);
	pass=end->next;
}

void run_unit(int candef) {
	if (tokis(pass,"{")) run_block();
	else if (candef && istypdef(pass)) deflocals();
	else if (tokis(pass,"if while")) {
		int wh=tokis(pass,"while");
		int jps=labels+=2;
		if (wh) label(jps);
		if (!tokis(pass->next,"(")) error("missing (");
		struct token *end=endbr(pass->next,NULL);
		struct tree *tree=gettree(pass->next,end->next);
		if (!tree) error("null condition");
		run_tree(tree,0);
		pop_cmp(size(tree->type));
		jmpn(jps-1);
		freetree(tree);
		pass=end->next;
		run_unit(0);
		if (wh || tokis(pass,"else")) jmp(jps);
		label(jps-1);
		if (!wh && tokis(pass,"else")) {
			pass=pass->next;
			run_unit(0);
			label(jps);
		}
	} else if (tokis(pass,"return")) {
		struct token *end=find_leftest(pass,NULL,";");
		if (!end) error("missing ;");
		struct tree *tree=gettree(pass->next,end);
		if (!tree) {
			ret_func();
			return;
		}
		if (tree->next) error("unexpected ,");
		assign_var(retvar,tree);
		freetree(tree);
		ret_func();
		pass=end->next;
	} else run_stat();
}

void run_block() {
	struct token *end=endbr(pass,NULL);
	if (!end) error("missing }");
	struct var *oldlastvar=lastvar;
	int oldstack=stack;
	pass=pass->next;
	while (pass!=end) run_unit(1);
	pass=pass->next;
	while (lastvar!=oldlastvar) del_var();
	setstack(oldstack-stack);
}

void run_func() {
	struct var *oldlastvar=lastvar;
 	struct vtype typ;
	retvar->type.bt=tmp_type(&pass);
	if (retvar->type.bt==tsame) error("same is a pseudo type");
 	retvar->type.pt=0;
	retvar->type.lv=1;
	while (tokis(pass,"*")) {
		retvar->type.pt++;
		pass=pass->next;
	}
	if (tokis(pass,"&")) {
		retvar->type.lv=2;
		pass=pass->next;
	}
	stack=0;
	pass=pass->next->next;
	while (!tokis(pass,")")) {
		typ.bt=tmp_type(&pass);
		if (!typ.bt) error("invalid type");
		if (typ.bt==tsame) error("same is a pseudo type");
		typ.pt=0;
		typ.lv=1;
		while (tokis(pass,"*")) {
			typ.pt++;
			pass=pass->next;
		}
		if (tokis(pass,"&")) {
			typ.lv=2;
			pass=pass->next;
		}
		setstack(-(typ.lv>1?PTR_SIZE:size(typ)));
		add_var(pass,typ,stack);
		pass=pass->next;
		if (tokis(pass,",")) pass=pass->next;
	}
	pass=pass->next;
	run_block();
	while (lastvar!=oldlastvar) del_var();
	ret_func();
}

void run_file() {
	while (pass) {
		if (tokis(pass,"extern")) {
			pass=pass->next;
			defglobs(1);
		} else if (tokis(pass,"global")) {
			pass=pass->next;
			defglobs(2);
		} else {
			struct token *tmp=pass;
			tmp_type(&tmp);
			while (tokis(tmp,"* &")) tmp=tmp->next;
			if (tokis(tmp->next,"(")) {
				tmp=pass;
				struct func *fun=add_func(0);
				if (tokis(pass,";")){
					pass=pass->next;
				} else if (tokis(pass,"{")) {
					pass=tmp;
					fun->loc=1;
					start_func(fun->name);
					run_func();
				} else error("missing ; or {");
			
			} else defglobs(0);
		}
	}
}
